﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Xml.Linq;
using System.Globalization;

namespace iTunesFastForward.Genpuid
{
    class MusicBrainzWebService
    {

        const string RELEASE_URL = "http://musicbrainz.org/ws/1/release/{0}?type=xml&inc=release-events+counts";
        const string TRACK_PUID_URL = "http://musicbrainz.org/ws/1/track/?type=xml&puid={0}";
        const string TRACK_URL = "http://musicbrainz.org/ws/1/track/{0}?type=xml&inc=releases";
        const string NS = "http://musicbrainz.org/ns/mmd-1.0#";

        ItemsCache _itemsCache = new ItemsCache();

        private string SendGet(string url)
        {
            try
            {
                var wc = new WebClient();
                return wc.DownloadString(url);
            }
            catch (WebException)
            {
                return null;
            }
        }

        private string GetNodeName(string name)
        {
            // faire par ex : {http://musicbrainz.org/ns/mmd-1.0#}metadata
            return string.Format("{0}{1}{2}{3}", "{", NS, "}", name);
        }

        public List<Track> GetTrackMBIDs(string puid)
        {

            string xml = SendGet(string.Format(TRACK_PUID_URL, puid));
            if (xml == null)
                return null;
            else
                return ParseTrackList(xml);

        }

        private List<Track> ParseTrackList(string xml)
        {
            List<Track> tracks = new List<Track>();

            XDocument doc = XDocument.Parse(xml);
            var tracklist = doc.Element(GetNodeName("metadata")).Element(GetNodeName("track-list"));

            if (tracklist == null)
            {
                // il n'y a pas d'enregistrement pour cette track, quitter
                return null;
            }

            foreach (var track in tracklist.Elements(GetNodeName("track")))
            {
                string mbid = track.Attribute("id").Value;

                if (_itemsCache.CheckTrack(mbid))
                    tracks.Add(_itemsCache.GetTrack(mbid));
                else
                {

                    Track t = new Track();
                    t.MBID = mbid;
                    t.Title = track.Element(GetNodeName("title")).Value;
                    if (track.Element(GetNodeName("duration")) == null)
                        t.Duration = 0;
                    else
                        t.Duration = int.Parse(track.Element(GetNodeName("duration")).Value);

                    var artist = track.Element(GetNodeName("artist"));

                    string artistid = artist.Attribute("id").Value;

                    if (_itemsCache.CheckArtist(artistid))
                        t.Artist = _itemsCache.GetArtist(artistid);
                    else
                    {
                        t.Artist = new Artist { 
                            MBID = artistid,
                            Name = artist.Element(GetNodeName("name")).Value 
                        };
                        _itemsCache.AddArtist(t.Artist);
                    }

                    var releases = track.Element(GetNodeName("release-list")).Elements(GetNodeName("release"));
                    
                    t.Releases = new List<TrackRelease>();

                    foreach (var release in releases)
                    {
                        string releaseid = release.Attribute("id").Value;

                        if (_itemsCache.CheckRelease(releaseid))
                            t.Releases.Add(
                                new TrackRelease
                                {
                                    Track = t,
                                    Release = _itemsCache.GetRelease(releaseid),
                                    TrackNumber = int.Parse(release.Element(GetNodeName("track-list")).Attribute("offset").Value) + 1
                                }
                                );
                        else
                        {
                            // récupérer la release en faisant une nouvelle requête
                            Release r = GetRelease(releaseid);

                            if (r != null)
                            {
                                t.Releases.Add(
                                    new TrackRelease
                                    {
                                        Track = t,
                                        Release = r,
                                        TrackNumber = int.Parse(release.Element(GetNodeName("track-list")).Attribute("offset").Value) + 1
                                    });
                            }
                        }
                    }

                    _itemsCache.AddTrack(t);
                    tracks.Add(t);
                }

            }
            return tracks;
        }

        public Release GetRelease(string releaseid)
        {
            string xml = SendGet(string.Format(RELEASE_URL, releaseid));
            if (xml == null)
                return null;

            XDocument doc = XDocument.Parse(xml);

            var release = doc.Element(GetNodeName("metadata")).Element(GetNodeName("release"));

            Release r = new Release();
            r.Title = release.Element(GetNodeName("title")).Value;
            r.MBID = release.Attribute("id").Value;

            var datereleases = release.Element(GetNodeName("release-event-list"));

            if (datereleases == null)
                r.FirstRelease = null;
            else
            {


                var date = datereleases.Element(GetNodeName("event")).Attribute("date").Value;

                // tenter de prendre une date complète
                DateTime d;
                int year;
                if (DateTime.TryParse(date, new CultureInfo("en-US"), DateTimeStyles.None, out d))
                {
                    r.FirstRelease = d;
                }
                // sinon juste l'année
                else if (int.TryParse(date, out year))
                {
                    try
                    {
                        r.FirstRelease = new DateTime(year, 1, 1);
                    }
                    catch (Exception)
                    {
                        r.FirstRelease = null;
                    }
                }
            }

            if (release.Element(GetNodeName("disc-list")) == null)
                r.TrackCount = 0;
            else
                r.TrackCount = int.Parse(release.Element(GetNodeName("disc-list")).Attribute("count").Value);
            _itemsCache.AddRelease(r);
            return r;

        }

    }
}
